﻿using System.Text;
using DimensionsHelper.Common.Contracts;
using DimensionsHelper.Common.Contracts.Services;

namespace DimensionsHelper.CommandLine.ConsoleTracer;

/// <summary>
///     向控制台报告Log信息。
/// </summary>
public static class Tracer
{
    private static TracerProgressBar? _currentProgress;

    static Tracer()
    {
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        Console.OutputEncoding = Encoding.Unicode;
    }

    /// <summary>
    ///     记录信息的文字颜色
    /// </summary>
    public static ConsoleColor LogForeground { get; set; } = ConsoleColor.DarkGray;


    /// <summary>
    ///     警告信息的文字颜色
    /// </summary>
    public static ConsoleColor WarnForeground { get; set; } = ConsoleColor.DarkYellow;


    /// <summary>
    ///     错误信息的文字颜色
    /// </summary>
    public static ConsoleColor ErrorForeground { get; set; } = ConsoleColor.Red;


    /// <summary>
    ///     报告信息时添加的前缀
    /// </summary>
    public static string TracerPrefix { get; set; } = "[Tracer] ";


    /// <summary>
    ///     当此值是true时，直接使用 <see cref="Console" /> 类来打印文字。
    /// </summary>
    private static bool NoWindow => Console.IsOutputRedirected;


    private static ConsoleColor GetConsoleColor(TracerCacheKind kind)
    {
        return kind switch
        {
            TracerCacheKind.Warning => WarnForeground,
            TracerCacheKind.Error => ErrorForeground,
            _ => LogForeground
        };
    }


    private static int GetLine()
    {
        return Console.CursorTop + Convert.ToInt32(Console.CursorLeft > 0);
    }


    private static void Report(TracerCacheKind kind, string format, params object?[] args)
    {
        if (NoWindow)
        {
            Console.OutputEncoding = Encoding.Default;
            Console.WriteLine($@"{TracerPrefix}{format}", args);
            return;
        }

        Console.OutputEncoding = Encoding.Unicode;
        int actualLine = Console.CursorTop;
        string message = string.Format(format, args);
        var line = new TracerLine(actualLine);
        line.Append(TracerPrefix).SetForeground(GetConsoleColor(kind));
        line.Append(message).SetForeground(GetConsoleColor(kind));
        Console.WriteLine();
    }


    /// <summary>
    ///     报告普通的记录信息。
    /// </summary>
    public static void Log(string format, params object?[] args)
    {
        Report(TracerCacheKind.Info, format, args);
    }


    /// <summary>
    ///     报告警告信息
    /// </summary>
    public static void Warn(string format, params object?[] args)
    {
        Report(TracerCacheKind.Warning, format, args);
    }


    /// <summary>
    ///     报告错误信息
    /// </summary>
    public static void Error(string format, params object?[] args)
    {
        Report(TracerCacheKind.Error, format, args);
    }


    /// <summary>
    ///     报告进度
    /// </summary>
    /// <param name="percent">进度值，0~100</param>
    /// <param name="format">消息模板</param>
    /// <param name="args">模板参数</param>
    public static void Progress(double percent, string format, params object?[] args)
    {
        // 没有控制台窗口时，忽略进度信息
        if (NoWindow)
        {
            return;
        }

        if (_currentProgress == null)
        {
            _currentProgress = new TracerProgressBar(GetLine());
            _currentProgress.Finished += static (_, _) =>
            {
                _currentProgress = null;
                Console.WriteLine();
            };
        }

        // 当百分比到达100时，currentProgress会变成null，所以要先更新文本内容。
        _currentProgress.UpdateLogContent(string.Format(format, args));
        _currentProgress.Percentage = percent;
    }


    /// <summary>
    ///     挂载报告回调函数并指定callback回调
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static void TraceReport<T>(T report, Action<T> callback) where T : IProjectService
    {
        callback(report);
    }


    public static int GetCharLength(char ch, int current)
    {
        if (ch == '\t')
        {
            return 8 - (current % 8);
        }

        return Console.OutputEncoding.GetByteCount(ch.ToString());
    }


    public static int GetDisplayLength(char[]? charArray)
    {
        return charArray?.Aggregate(0, (current, ch) => current + GetCharLength(ch, current)) ?? 0;
    }


    public static int GetDisplayLength(string? text)
    {
        return GetDisplayLength(text?.ToCharArray());
    }


    public static string SubString(string text, int displayLength)
    {
        char[] charArray = text.ToCharArray();
        StringBuilder builder = new();
        int len = 0;

        foreach (char ch in charArray)
        {
            len += GetCharLength(ch, len);

            if (len >= displayLength)
            {
                break;
            }

            builder.Append(ch);
        }

        return builder.ToString();
    }


    private enum TracerCacheKind
    {
        Info,
        Warning,
        Error
    }


    public static IReportService GetService(ReportLevel reportLevel, string section) =>
        new ReportService(reportLevel, section);


    private class ReportService(ReportLevel reportLevel, string section) : IReportService
    {

        public void Directory(string localDirectory)
        {
            Tracer.Log("[{0}] 本地文件：{0}", section, localDirectory);
        }

        public void Directory(string localDirectory, string format, params object?[] args)
        {
            Tracer.Log("[{0}] {1}: {2}", section, string.Format(format, args), localDirectory);
        }

        public void Error(string format, params object?[] args)
        {
            Tracer.Error($"[{section}] {string.Format(format, args)}");
        }

        public void Log(ReportLevel level, string format, params object?[] args)
        {
            if (level > reportLevel)
            {
                return;
            }

            Tracer.Log($"[{section}] {string.Format(format, args)}");
        }

        public void Log(string format, params object?[] args)
        {
            Tracer.Log($"[{section}] {string.Format(format, args)}");
        }

        public void Progress(double progress)
        {
            Tracer.Progress(progress, string.Empty);
        }

        public void Progress(double progress, string format, params object?[] args)
        {
            Tracer.Progress(progress, $"[{section}] {string.Format(format, args)}");
        }

        public void Warn(string format, params object?[] args)
        {
            Tracer.Warn($"[{section}] {string.Format(format, args)}");
        }

        public IProgressEnumerable<T> WithProgress<T>(IEnumerable<T> source, int length, Func<T, double, string>? messageGetter)
        {
            return new ProgressEnumerable<T>(source, messageGetter);
        }
    }

}